package org.ecloud.oauth.server.config;

import java.io.UnsupportedEncodingException;

import org.apache.commons.lang3.StringUtils;
import org.ecloud.encrypt.Base64;
import org.ecloud.encrypt.EncryptUtil;
import org.ecloud.oauth.exception.ExpiredAccountException;
import org.ecloud.oauth.exception.InactiveException;
import org.ecloud.oauth.exception.LockedAccountException;
import org.ecloud.oauth.exception.UnknownAccountException;
import org.ecloud.utils.BeanUtil;
import org.ecloud.utils.StringUtil;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;

@Configuration
@ConfigurationProperties("user")
public class UserConfig {
	
	public static final String DEF_TABLE_NAME = "user";
	public static final String FIELD_DEF_UID = "id_";
	public static final String FIELD_DEF_ACCOUNT = "account_";
	public static final String FIELD_DEF_PASSWORD = "password_";
	public static final String FIELD_DEF_STATUS = "status_";
	public static final String FIELD_DEF_CREATE_TIME = "create_time_";
	public static final String FIELD_DEF_UPDATE_TIME = "update_time_";
	
	public static final String DEF_UID = "uid";
	public static final String DEF_ACCOUNT = "account";
	public static final String DEF_PASSWORD = "password";
	public static final String DEF_STATUS = "status";
	public static final String DEF_CREATE_TIME = "createtime";
	public static final String DEF_UPDATE_TIME = "updatetime";
	
	private String tableName = DEF_TABLE_NAME;
	private Field field;
	/**
	 * 必须包含返回字段：uid/account/password/status
	 * 所有字段都是字符类型
	 * 若该字段有值，则其它属性不生效
	 * 例：select u.id_ uid, u.account_ account, u.password_ password, e.status_ status from e_party_user u left join e_party_employee e on e.id_ = u.id_ where u.account_=?
	 */
	private String sql;
	
	/**
	 * clientEncrypt:密码客户端加密
	 */
	private Encrypt clientEncrypt;
	/**
	 * serverEncrypt:密码服务端加密
	 */
	private Encrypt serverEncrypt;
	
	/**
	 * statusInfo:状态对应值
	 */
	private StatusInfo statusInfo;
	
	private boolean validCodeEnabled = false;
	private int validRetry = 3;
	private Lock lock = new Lock();
	
	private Security security;
	
	public String getTableName() {
		return tableName;
	}
	public void setTableName(String tableName) {
		this.tableName = tableName;
	}
	public Field getField() {
		if(BeanUtil.isEmpty(field)){
			return new Field();
		}
		
		return field;
	}
	public void setField(Field field) {
		this.field = field;
	}
	public String getSql() {
		if(StringUtils.isNotBlank(sql))
			return sql;
		
		StringBuilder builder = new StringBuilder();
		builder.append("select ");
		builder.append(this.getField().getUid()).append(" uid, ");
		builder.append(this.getField().getAccount()).append(" account, ");
		builder.append(this.getField().getPassword()).append(" password, ");
		builder.append(this.getField().getCreateTime()).append(" createtime, ");
		builder.append(this.getField().getUpdateTime()).append(" updatetime ");
		if(StringUtil.isNotBlank(this.getField().getStatus())){
			builder.append(",").append(this.getField().getStatus()).append(" status ");
		}else{
			builder.append(", -1").append(" status ");
		}
		builder.append(" from ");
		builder.append(this.getTableName());
		builder.append(" where ").append(this.getField().getAccount()).append("=?");
		
		return builder.toString();
	}
	public void setSql(String sql) {
		this.sql = sql;
	}
	public Encrypt getClientEncrypt() {
		return clientEncrypt;
	}
	public void setClientEncrypt(Encrypt clientEncrypt) {
		this.clientEncrypt = clientEncrypt;
	}
	public Encrypt getServerEncrypt() {
		return serverEncrypt;
	}
	public void setServerEncrypt(Encrypt serverEncrypt) {
		this.serverEncrypt = serverEncrypt;
	}
	public StatusInfo getStatusInfo() {
		return statusInfo;
	}
	public void setStatusInfo(StatusInfo statusInfo) {
		this.statusInfo = statusInfo;
	}
	public boolean isValidCodeEnabled() {
		return validCodeEnabled;
	}
	public void setValidCodeEnabled(boolean validCodeEnabled) {
		this.validCodeEnabled = validCodeEnabled;
	}
	public int getValidRetry() {
		return validRetry;
	}
	public void setValidRetry(int validRetry) {
		this.validRetry = validRetry;
	}
	public Lock getLock() {
		return lock;
	}
	public void setLock(Lock lock) {
		this.lock = lock;
	}
	public Security getSecurity() {
		return security;
	}
	public void setSecurity(Security security) {
		this.security = security;
	}
	
	/**
	 * 密码加密
	 *
	 * @param originPassword
	 * @return 
	 */
	public String encrypt(String originPassword){
		String password = originPassword;
		
		if(BeanUtil.isNotEmpty(clientEncrypt)){
			if(clientEncrypt.isEncrypt()){
				if(Encrypt.BASE_64.equalsIgnoreCase(serverEncrypt.getEncryptName())){
					try {
						password = originPassword = Base64.decodeBase64(originPassword);
					} catch (UnsupportedEncodingException e) {
						throw new RuntimeException("字符集不支持");
					}
				} else {
					throw new RuntimeException("加密传输策略不支持");
				}
			}
		}
		
		if(BeanUtil.isNotEmpty(serverEncrypt)){
			if(serverEncrypt.isEncrypt()){
				if(Encrypt.SHA_256.equalsIgnoreCase(serverEncrypt.getEncryptName())){
					password = EncryptUtil.encryptSha256(password);
				} else if(Encrypt.MD5.equalsIgnoreCase(serverEncrypt.getEncryptName())){
					password = EncryptUtil.encryptMd5(password);
				} else {
					throw new RuntimeException("密码策略不支持");
				}
			}
		}
		
		return password;
	}
	
	/**
	 * 用户状态检验
	 *
	 * @param status 
	 */
	public void verifyStatus(String status){
		/*
		actived: actived
		inactive: inactive
		locked: locked
		disabled: disabled
		expired: expired
		deleted: deleted
		*/
		
		if(BeanUtil.isEmpty(statusInfo)){
			// 没有用户状态字段
			return;
		}
		
		if(StringUtil.isBlank(status)){
			throw new RuntimeException("用户状态值为空");
		}
		
		statusInfo.verify();
		
		// 用户未激活
		if(status.equalsIgnoreCase(statusInfo.getInactive())){
			throw new InactiveException("用户未激活");
		}
		
		// 被锁定
		if(status.equalsIgnoreCase(statusInfo.getLocked())){
			throw new LockedAccountException("用户被锁定");
		}
		
		// 过期
		if(status.equalsIgnoreCase(statusInfo.getExpired())){
			throw new ExpiredAccountException("用户已过期");
		}
		
		// 禁用
		if(status.equalsIgnoreCase(statusInfo.getDisabled())){
			throw new ExpiredAccountException("用户被禁用");
		}
		
		// 被删除
		if(status.equalsIgnoreCase(statusInfo.getDeleted())){
			throw new UnknownAccountException("用户被删除");
		}
	}

	public static class Field {
		private String uid = FIELD_DEF_UID;
		private String account = FIELD_DEF_ACCOUNT;
		private String password = FIELD_DEF_PASSWORD;
		private String status;
		private String createTime;
		private String updateTime;
		
		public String getUid() {
			return uid;
		}
		public void setUid(String uid) {
			this.uid = uid;
		}
		public String getAccount() {
			return account;
		}
		public void setAccount(String account) {
			this.account = account;
		}
		public String getPassword() {
			return password;
		}
		public void setPassword(String password) {
			this.password = password;
		}
		public String getStatus() {
			return status;
		}
		public void setStatus(String status) {
			this.status = status;
		}
		public String getCreateTime() {
			return createTime;
		}
		public void setCreateTime(String createTime) {
			this.createTime = createTime;
		}
		public String getUpdateTime() {
			return updateTime;
		}
		public void setUpdateTime(String updateTime) {
			this.updateTime = updateTime;
		}
	}
	
	public static class Encrypt {
		public static final String BASE_64 = "base64";
		public static final String SHA_256 = "sha256";
		public static final String MD5 = "md5";
		
		private boolean encrypt = false;
		private String encryptName = SHA_256;
		public boolean isEncrypt() {
			return encrypt;
		}
		public void setEncrypt(boolean encrypt) {
			this.encrypt = encrypt;
		}
		public String getEncryptName() {
			return encryptName;
		}
		public void setEncryptName(String encryptName) {
			this.encryptName = encryptName;
		}
	}
	
	public static class StatusInfo {
		private String tableName;
		private String fieldId;
		private String field;
		private String actived;
		private String inactive;
		private String locked;
		private String disabled;
		private String expired;
		private String deleted;
		
		public void verify(){
			if(StringUtil.isBlank(locked)
					|| StringUtil.isBlank(disabled)
					|| StringUtil.isBlank(expired)
					|| StringUtil.isBlank(tableName)
					|| StringUtil.isBlank(fieldId)
					|| StringUtil.isBlank(field)
					){
				throw new RuntimeException("用户状态信息未配置");
			}
		}
		
		public String getLockSql(){
			verify();
			
			StringBuilder sqlBuilder = new StringBuilder();
			sqlBuilder.append("update ");
			sqlBuilder.append(tableName);
			sqlBuilder.append(" set ");
			sqlBuilder.append(field).append("=? ");
			sqlBuilder.append("where ");
			sqlBuilder.append(fieldId);
			sqlBuilder.append("=?");
			
			return sqlBuilder.toString();
		}
		
		public String getTableName() {
			return tableName;
		}
		public void setTableName(String tableName) {
			this.tableName = tableName;
		}
		public String getFieldId() {
			return fieldId;
		}

		public void setFieldId(String fieldId) {
			this.fieldId = fieldId;
		}

		public String getField() {
			return field;
		}
		public void setField(String field) {
			this.field = field;
		}
		public String getActived() {
			return actived;
		}
		public void setActived(String actived) {
			this.actived = actived;
		}
		public String getInactive() {
			return inactive;
		}
		public void setInactive(String inactive) {
			this.inactive = inactive;
		}
		public String getLocked() {
			return locked;
		}
		public void setLocked(String locked) {
			this.locked = locked;
		}
		public String getDisabled() {
			return disabled;
		}
		public void setDisabled(String disabled) {
			this.disabled = disabled;
		}
		public String getExpired() {
			return expired;
		}
		public void setExpired(String expired) {
			this.expired = expired;
		}
		public String getDeleted() {
			return deleted;
		}
		public void setDeleted(String deleted) {
			this.deleted = deleted;
		}
	}
	
	public static class Lock {
		public static final String AUTO = "auto";
		public static final String MANUAL = "manual";
		
		private String tableName;
		private String fieldId = "id_";
		private String fieldAccount = "account_";
		private String fieldMode = "mode_";
		private String fieldLockTime = "lock_time_";
		private String fieldUnlockTime = "unlock_time_";
		
		private int retry = 5;
		private String mode = AUTO;
		private int time = 600; // second
		
		public void verify(){
			if(StringUtil.isBlank(tableName)
					|| StringUtil.isBlank(fieldId)
					|| StringUtil.isBlank(fieldAccount)
					|| StringUtil.isBlank(fieldMode)
					|| StringUtil.isBlank(fieldLockTime)
					|| StringUtil.isBlank(fieldUnlockTime)
					){
				throw new RuntimeException("用户锁定信息未配置");
			}
			
			if(!mode.equalsIgnoreCase(AUTO) && !mode.equalsIgnoreCase(MANUAL)){
				throw new RuntimeException("用户锁定锁定模式不支持");
			}
		}
		
		public String getSql(){
			verify();
			
			StringBuilder sqlBuilder = new StringBuilder();
			sqlBuilder.append("insert ");
			sqlBuilder.append(tableName);
			sqlBuilder.append("(");
			sqlBuilder.append(fieldId).append(",");
			sqlBuilder.append(fieldAccount).append(",");
			sqlBuilder.append(fieldMode).append(",");
			sqlBuilder.append(fieldLockTime).append(",");
			sqlBuilder.append(fieldUnlockTime);
			sqlBuilder.append(")");
			sqlBuilder.append(" values(?,?,?,?,?)");
			
			return sqlBuilder.toString();
		}
		
		public String getCountSql(){
			verify();
			
			StringBuilder sqlBuilder = new StringBuilder();
			sqlBuilder.append("select count(0)");
			sqlBuilder.append(" from ");
			sqlBuilder.append(tableName);
			sqlBuilder.append(" where ");
			sqlBuilder.append(fieldAccount).append("=?");
			
			return sqlBuilder.toString();
		}
		
		public String getRemoveSql(){
			verify();
			
			StringBuilder sqlBuilder = new StringBuilder();
			sqlBuilder.append("delete from ");
			sqlBuilder.append(tableName);
			sqlBuilder.append(" where ");
			sqlBuilder.append(fieldAccount).append("=?");
			
			return sqlBuilder.toString();
		}
		
		public String getTableName() {
			return tableName;
		}
		public void setTableName(String tableName) {
			this.tableName = tableName;
		}
		public String getFieldAccount() {
			return fieldAccount;
		}
		public void setFieldAccount(String fieldAccount) {
			this.fieldAccount = fieldAccount;
		}
		public String getFieldMode() {
			return fieldMode;
		}
		public void setFieldMode(String fieldMode) {
			this.fieldMode = fieldMode;
		}
		public String getFieldLockTime() {
			return fieldLockTime;
		}
		public void setFieldLockTime(String fieldLockTime) {
			this.fieldLockTime = fieldLockTime;
		}
		public String getFieldUnlockTime() {
			return fieldUnlockTime;
		}
		public void setFieldUnlockTime(String fieldUnlockTime) {
			this.fieldUnlockTime = fieldUnlockTime;
		}
		public int getRetry() {
			return retry;
		}
		public void setRetry(int retry) {
			this.retry = retry;
		}
		public String getMode() {
			return mode;
		}
		public void setMode(String mode) {
			this.mode = mode;
		}
		public int getTime() {
			return time;
		}
		public void setTime(int time) {
			this.time = time;
		}
	}
	
	public static class Security {
		private String tableName;
		private String fieldUse = "is_use_comp_";
		private String fieldDefault = "is_default_";
		private String fieldLimitTime = "time_limit_";
		private String fieldLimitUpdTime = "upd_tlimit_";
		
		public void verify(){
			if(StringUtil.isBlank(tableName)
					|| StringUtil.isBlank(fieldUse)
					|| StringUtil.isBlank(fieldDefault)
					|| StringUtil.isBlank(fieldLimitTime)
					|| StringUtil.isBlank(fieldLimitUpdTime)
					){
				throw new RuntimeException("用户密码信息未配置");
			}
		}
		
		public String getSql(){
			verify();
			
			StringBuilder sqlBuilder = new StringBuilder();
			sqlBuilder.append("select ");
			sqlBuilder.append(fieldUse).append(",");
			sqlBuilder.append(fieldDefault).append(",");
			sqlBuilder.append(fieldLimitTime).append(",");
			sqlBuilder.append(fieldLimitUpdTime);
			sqlBuilder.append(" from ");
			sqlBuilder.append(tableName);
			sqlBuilder.append(" where ");
			sqlBuilder.append(fieldUse).append("='Y'");
			sqlBuilder.append(" and ");
			sqlBuilder.append(fieldDefault).append("='Y'");
			
			return sqlBuilder.toString();
		}
		
		public String getTableName() {
			return tableName;
		}
		public void setTableName(String tableName) {
			this.tableName = tableName;
		}
		public String getFieldUse() {
			return fieldUse;
		}
		public void setFieldUse(String fieldUse) {
			this.fieldUse = fieldUse;
		}
		public String getFieldDefault() {
			return fieldDefault;
		}
		public void setFieldDefault(String fieldDefault) {
			this.fieldDefault = fieldDefault;
		}
		public String getFieldLimitTime() {
			return fieldLimitTime;
		}
		public void setFieldLimitTime(String fieldLimitTime) {
			this.fieldLimitTime = fieldLimitTime;
		}
		public String getFieldLimitUpdTime() {
			return fieldLimitUpdTime;
		}
		public void setFieldLimitUpdTime(String fieldLimitUpdTime) {
			this.fieldLimitUpdTime = fieldLimitUpdTime;
		}
		
	}
	
}
